//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2007
//
// 'Select' tool.
//

# include "StdAfx.h"
# include "Resource.h"
# include "Tool.h"
# include "Selection.h"
# include "Types.h"
# include "MapView.h"
# include "Load.h"
# include "Menu.h"
# include "RgnProperties.h"
# include "RasterProperties.h"
# include "EditNumber.h"
# include "MoveObject.h"
# include "MoveImage.h"
# include "Rename.h"
# include "SetComment.h"
# include "ChangeType.h"
# include "SetRgnLevels.h"
# include "ChangeCity.h"
# include "ChangeZip.h"
# include "SetRgnExtra.h"
# include "Vectorize.h"
# include "ConvertTrack.h"
# include "ShowHideAttachment.h"
# include "JoinObjects.h"
# include "ExtractElement.h"
# include "MergeObjects.h"
# include "ClosePolyline.h"
# include "ReversePoints.h"
# include "TrimByPolygon.h"
# include "CombinedAction.h"
# include "WebMap.h"
# include "Position.h"
# include "Scale.h"
# include "Levels.h"
# include "SelectLevels.h"
# include "EditText.h"
# include "SelectList.h"
# include "DDBitmap.h"
# include "Paste.h"
# include "fast_floor.h"
# include "Globals.h"

static
bool _IsSelMultipleOn (DWORD _dwKeys) {
	if (g_bMSelByShift)
		return (_dwKeys & MK_SHIFT)   != 0;
	else
		return (_dwKeys & MK_CONTROL) != 0;
}

static
bool _IsSelElementOn (DWORD _dwKeys) {
	if (g_bMSelByShift)
		return (_dwKeys & MK_CONTROL) != 0;
	else
		return (_dwKeys & MK_SHIFT)   != 0;
}

////////////////////////////////////////////////////////////

class CSelectTool : public CTool {
  public:
	CSelectTool () :
		CTool (toolSelect),
		m_pDraggedBuffer (NULL),
		m_bFirstMoveInDND (false)
	{}

	virtual bool Init ();
	virtual void Destroy ();

	virtual void OnActivate ();

	virtual void OnLDown   (int _x, int _y, DWORD _dwKeys);
	virtual void OnLUp     (int _x, int _y, DWORD _dwKeys);
	virtual void OnMove    (int _x, int _y, DWORD _dwKeys);
	virtual void OnRUp     (int _x, int _y, DWORD _dwKeys);
	virtual void OnLDblClk (int _x, int _y, DWORD _dwKeys);

	virtual void OnPaint (HDC _hDC, const RECT & _rect);

	virtual void OnCtxMenu (int _x, int _y);

	virtual bool OnCommand (WORD _wID);

  private:
	CDDBitmap * m_pDraggedBuffer;
	bool m_bFirstMoveInDND;
};

DECLARE_TOOL (CSelectTool);

static HCURSOR g_hcurDragReady;
static HCURSOR g_hcurDrag;

static HPEN g_hpenGroupSelection;

static bool g_bFrameSelectionMode = false;

bool CSelectTool::Init () {
	g_hcurDrag = ::LoadCursor (g_hInst, MAKEINTRESOURCE (IDC_CursorDrag));

	g_hpenGroupSelection = ::CreatePen (PS_SOLID, 1, c_colGray);

	return true;
}

void CSelectTool::Destroy () {
	if (g_hpenGroupSelection)
		::DeleteObject (g_hpenGroupSelection);

	delete m_pDraggedBuffer;
}

void CSelectTool::OnActivate () {
	SetPermanentCursor (g_hArrowCursor);
    g_bFrameSelectionMode = false;
}

void CSelectTool::OnLDown (int _x, int _y, DWORD _dwKeys) {
	g_bFrameSelectionMode = ! SelectObjectAt (_x, _y, _IsSelMultipleOn (_dwKeys), _IsSelElementOn (_dwKeys));

	SetPermanentCursor (g_bFrameSelectionMode ? g_hArrowCursor : g_hcurDrag);

	m_bFirstMoveInDND = true;
	StartDND (_x, _y);
}

void CSelectTool::OnLUp (int _x, int _y, DWORD _dwKeys) {
	SetPermanentCursor (g_hArrowCursor);

	if (! CTool::s_bDND)
		return;
	StopDND ();

    if (g_bFrameSelectionMode) {
		g_bFrameSelectionMode = false;

        if (
			// TODO: allow shift over double-click area?
			CTool::s_lX_DND_Start != CTool::s_lX_DND ||
            CTool::s_lY_DND_Start != CTool::s_lY_DND
		) {
			// Prepare the selection rectangle.
	        rect_t rectSelection;

            point_t pt1;
            ScreenToPt (CTool::s_lX_DND_Start, CTool::s_lY_DND_Start, pt1);

			point_t pt2;
            ScreenToPt (CTool::s_lX_DND,       CTool::s_lY_DND,       pt2);

            if (pt1.x < pt2.x) {
                rectSelection.x0 = pt1.x;
                rectSelection.x1 = pt2.x;
            } else {
                rectSelection.x0 = pt2.x;
                rectSelection.x1 = pt1.x;
            }

            if (pt1.y < pt2.y) {
                rectSelection.y0 = pt1.y;
                rectSelection.y1 = pt2.y;
            } else {
                rectSelection.y0 = pt2.y;
                rectSelection.y1 = pt1.y;
            }

            SelectObjectsInsideRect (rectSelection, _IsSelMultipleOn (_dwKeys), _IsSelElementOn (_dwKeys));
        }

		Redraw ();
		UpdateMenu ();
    } else {
		const long lXOffset = CTool::s_lX_DND - CTool::s_lX_DND_Start;
		const long lYOffset = CTool::s_lY_DND - CTool::s_lY_DND_Start;
		if (lXOffset == 0 && lYOffset == 0)
			return;

        if (
			::labs (lXOffset) <= 2*::GetSystemMetrics (SM_CXDOUBLECLK) &&
			::labs (lYOffset) <= 2*::GetSystemMetrics (SM_CYDOUBLECLK) &&
			::GetTickCount () - CTool::s_dwTime_DND_Start <= ::GetDoubleClickTime ()
		) {
			SetSelectionOffset (0, 0);
			Redraw ();
			return;
		}

		point_t pointShift;
		pointShift.x =  (lXOffset)/g_kX;
		pointShift.y = -(lYOffset)/g_kY;

		const size_t cLevelIdx = GetZoomLevel (g_fScale);
		if (cLevelIdx != -1)
			g_map.pTypeSet->pfRoundPoint (g_map, cLevelIdx, pointShift);

		SetSelectionOffset (0, 0);

		if (pointShift.x == 0 && pointShift.y == 0) {
			Redraw ();
			return;
		}

		const size_t cSelItems = g_Selection.size ();
		if (cSelItems == 0)
			return;

		const int iDepth = BeginCombinedAction ();

		for (size_t cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
			const find_info_t & SelItem = g_Selection [cSelItem];
			if (SelItem.pLayer) {
				if (SelItem.iRgn->IsDeleted () || SelItem.iRgn->IsWTR ())
					continue;

				MoveObject (& * SelItem.iRgn, SelItem.cElementOrOrigin, pointShift);
			} else {
				assert (SelItem.pRenderer != NULL);
				MoveImage (SelItem.pRenderer, pointShift);
			}
		}

		EndCombinedAction (iDepth, GetStr (IDS_MOVE_MULTIPLE));

		Refresh ();
	}
}

void CSelectTool::OnMove (int _x, int _y, DWORD _dwKeys) {
	if (! s_bDND) {
		SetPermanentCursor (g_hArrowCursor);
		return;
	}

	SetPermanentCursor (g_bFrameSelectionMode ? g_hArrowCursor : g_hcurDrag);

    if (! g_bFrameSelectionMode) {
		// Round the offset to Garmin units.
		const size_t cLevelIdx = GetZoomLevel (g_fScale);
		if (cLevelIdx != -1 && cLevelIdx < g_map.Levels.size ()) {
			point_t pointShift ((_x - CTool::s_lX_DND_Start)/g_kX, -(_y - CTool::s_lY_DND_Start)/g_kY);
			g_map.pTypeSet->pfRoundPoint (g_map, cLevelIdx, pointShift);
			_x = CTool::s_lX_DND_Start + fast_floor (pointShift.x*g_kX + .5f);
			_y = CTool::s_lY_DND_Start - fast_floor (pointShift.y*g_kY + .5f);
		}

		SetSelectionOffset (_x - CTool::s_lX_DND_Start, _y - CTool::s_lY_DND_Start);
	}

	Redraw ();

	//
	// Redraw dragged images with semi-transparency.
	//
	if (g_bShowAttachments) {
		bool bHaveImages = false;
		size_t cSelItem;
		for (cSelItem = 0; cSelItem < g_Selection.size (); ++ cSelItem) {
			const find_info_t & SelItem = g_Selection [cSelItem];
			if (SelItem.pRenderer) {
				if (SelItem.pRenderer->bDeleted || ! SelItem.pRenderer->bVisible)
					continue;
				bHaveImages = true;
			}
		}

		if (bHaveImages) {
			const int c_nImageDragW = 200;

			if (m_bFirstMoveInDND) {
				m_bFirstMoveInDND = false;

				if (m_pDraggedBuffer == NULL)
					m_pDraggedBuffer = new CDDBitmap (c_nImageDragW*2, c_nImageDragW*2);

				// Fill background.
				RECT rect = {0, 0, c_nImageDragW*2, c_nImageDragW*2};
				::FillRect (m_pDraggedBuffer->GetDC (), & rect, g_hbrWhite);

				point_t pt0, pt1;
				ScreenToPt (CTool::s_lX_DND_Start - c_nImageDragW, CTool::s_lY_DND_Start + c_nImageDragW, pt0);
				ScreenToPt (CTool::s_lX_DND_Start + c_nImageDragW, CTool::s_lY_DND_Start - c_nImageDragW, pt1);
				const rect_t rectImageDrag (pt0.x, pt0.y, pt1.x, pt1.y);

				for (size_t cSelItem = 0; cSelItem < g_Selection.size (); ++ cSelItem) {
					const find_info_t & SelItem = g_Selection [cSelItem];
					if (SelItem.pRenderer) {
						if (SelItem.pRenderer->bDeleted)
							continue;

						SelItem.pRenderer->Draw (
							m_pDraggedBuffer->GetDC (),
							rectImageDrag,
							-(CTool::s_lX_DND_Start - c_nImageDragW),
							-(CTool::s_lY_DND_Start - c_nImageDragW)
						);
					}
				}
			}

			if (m_pDraggedBuffer) {
				CClientDC dcWindow (g_hMapWnd);
				m_pDraggedBuffer->AlphaBlend (
					dcWindow,
					_x - c_nImageDragW,	_y - c_nImageDragW, c_nImageDragW*2, c_nImageDragW*2,
					0,                  0,                  c_nImageDragW*2, c_nImageDragW*2,
					96
				);
			}
		}
	}
}

static long g_lRClickX;
static long g_lRClickY;

void CSelectTool::OnRUp (int _x, int _y, DWORD _dwKeys) {
	g_lRClickX = _x;
	g_lRClickY = _y;

	SelectObjectAt (_x, _y, _IsSelMultipleOn (_dwKeys), _IsSelElementOn (_dwKeys));

	CSelectTool::OnCtxMenu (_x, _y);
}

void CSelectTool::OnCtxMenu (int _x, int _y) {
	if (g_bLoading)
		return;

	const HMENU hPopupMenu = LoadMenu (IDM_POPUP);
	CMenuHolder hPopupMenu_Holder (hPopupMenu);

	HMENU hMenu = NULL;

	if (! g_Selection.empty ()) {
		size_t cImages      = 0;
		size_t cWaypoints   = 0;
		size_t cTracks      = 0;
		size_t cRoutes      = 0;
		size_t cObjects     = 0;
		size_t cFullObjects = 0;
		size_t cPolygons    = 0;
		size_t cPolylines   = 0;
		size_t cPolylinesE  = 0;
		size_t cHasAddress  = 0;
		size_t cRoads       = 0;

		const size_t cSelItems = g_Selection.size ();
		for (size_t cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
			const find_info_t & SelItem = g_Selection [cSelItem];
			if (SelItem.pRenderer) {
				if (! SelItem.pRenderer->bDeleted)
					++ cImages;
			} else if (SelItem.pLayer && ! SelItem.iRgn->IsDeleted ()) {
				if (SelItem.iRgn->IsWTR ()) {
					if (SelItem.iRgn->IsTrack ())
						++ cTracks;
					else if (SelItem.iRgn->IsRoute ())
						++ cRoutes;
					else if (SelItem.iRgn->IsWaypoint ())
						++ cWaypoints;
				} else {
					++ cObjects;
					if (SelItem.cElementOrOrigin == -1) {
						++ cFullObjects;
						if (SelItem.iRgn->IsPolyline ()) {
							++ cPolylines;
							if (g_map.pTypeSet->pfIsRoad (* SelItem.iRgn))
								++ cRoads;
						}
						if (SelItem.iRgn->IsPolygon ())
							++ cPolygons;
						if (g_map.pTypeSet->pfHasAddress (* SelItem.iRgn))
							++ cHasAddress;
					} else {
						if (SelItem.iRgn->IsPolyline ())
							++ cPolylinesE;
					}
				}
			}
		}

		if (cImages > 0 && cWaypoints + cTracks + cRoutes + cObjects == 0) {
			hMenu = ::GetSubMenu (hPopupMenu, smpRaster);
			assert (hMenu);
			::SetMenuDefaultItem (hMenu, ID_RASTER_PROPERTIES, MF_BYCOMMAND);

			const bool bHasCorrection = g_Selection.size () == 1 && g_Selection [0].pRenderer && ! (g_Selection [0].pRenderer->ptCorrectionShift == point_t (0, 0));
			::EnableMenuItem (hMenu, ID_EDIT_REVERT_CORRECTION, MF_BYCOMMAND | (bHasCorrection ? MF_ENABLED : MF_GRAYED));

			::EnableMenuItem (hMenu, ID_RASTER_PROPERTIES,      MF_BYCOMMAND | (cImages == 1   ? MF_ENABLED : MF_GRAYED));

# ifndef _DEBUG
			::DeleteMenu     (hMenu, ID_EDIT_VECTORIZE, MF_BYCOMMAND);
			DeleteDuplicatedMenuSeparators (hMenu);
# endif // _DEBUG
		} else {
			if (cWaypoints + cTracks + cRoutes > 0 && cImages + cObjects == 0) {
				hMenu = ::GetSubMenu (hPopupMenu, smpWTR);
				assert (hMenu);
				::SetMenuDefaultItem (hMenu, ID_RGN_PROPERTIES, MF_BYCOMMAND);
				
				::EnableMenuItem (hMenu, ID_EDIT_CONVERT_TO_POI,      MF_BYCOMMAND | (cWaypoints > 0 && (cTracks + cRoutes == 0) ? MF_ENABLED : MF_GRAYED));

				::EnableMenuItem (hMenu, ID_EDIT_CONVERT_TO_POLYLINE, MF_BYCOMMAND | (cWaypoints == 0 && (cTracks + cRoutes) > 0 ? MF_ENABLED : MF_GRAYED));
				::EnableMenuItem (hMenu, ID_EDIT_CONVERT_TO_POLYGON,  MF_BYCOMMAND | (cWaypoints == 0 && (cTracks + cRoutes) > 0 ? MF_ENABLED : MF_GRAYED));

				::EnableMenuItem (hMenu, ID_RGN_PROPERTIES,           MF_BYCOMMAND | (cWaypoints + cTracks + cRoutes == 1        ? MF_ENABLED : MF_GRAYED));

				if (cTracks + cRoutes == 0) {
					::DeleteMenu     (hMenu, ID_VIEW_SHOW_DIRECTION, MF_BYCOMMAND);
				} else {
					::EnableMenuItem (hMenu, ID_VIEW_SHOW_DIRECTION, MF_BYCOMMAND | (cSelItems == 1 ? MF_ENABLED : MF_GRAYED));
					if (cTracks + cRoutes == 1) {
						const rgn_t & rgn = * g_Selection [0].iRgn;
						const bool bShowDirection = rgn.pFileRef && rgn.pFileRef->bShowDirection;
						::CheckMenuItem (hMenu, ID_VIEW_SHOW_DIRECTION, MF_BYCOMMAND | (bShowDirection ? MF_CHECKED : MF_UNCHECKED));
					}
				}
			} else if (cObjects > 0 && cWaypoints + cTracks + cRoutes + cImages == 0) {
				hMenu = ::GetSubMenu (hPopupMenu, smpObject);
				assert (hMenu);
				::SetMenuDefaultItem (hMenu, ID_RGN_PROPERTIES, MF_BYCOMMAND);

				const UINT uiFlags = (g_Selection.size () == 1 && g_Selection [0].cElementOrOrigin == -1) ? MF_BYCOMMAND | MF_ENABLED : MF_BYCOMMAND | MF_GRAYED;
				::EnableMenuItem (hMenu, ID_TOOL_EDIT_NODES, uiFlags);
				::EnableMenuItem (hMenu, ID_RGN_PROPERTIES,  uiFlags);

				::EnableMenuItem (hMenu, ID_EDIT_PASTE,           MF_BYCOMMAND | (! IsClipboardEmpty () ? MF_ENABLED : MF_GRAYED));

				::EnableMenuItem (hMenu, ID_EDIT_KIND_POLYLINE,   MF_BYCOMMAND | (cPolylines == 0 && cPolygons > 0  && IsChangeTypeAppliable () ? MF_ENABLED : MF_GRAYED));
				::EnableMenuItem (hMenu, ID_EDIT_KIND_POLYGON,    MF_BYCOMMAND | (cPolylines > 0  && cPolygons == 0 && IsChangeTypeAppliable () ? MF_ENABLED : MF_GRAYED));

//				::EnableMenuItem (hMenu, ID_EDIT_LABEL,     );
				::EnableMenuItem (hMenu, ID_EDIT_TYPE,            MF_BYCOMMAND | (IsChangeTypeAppliable ()      ? MF_ENABLED : MF_GRAYED));
				::EnableMenuItem (hMenu, ID_EDIT_END_LEVEL,       MF_BYCOMMAND | (cFullObjects != 0             ? MF_ENABLED : MF_GRAYED));

				::EnableMenuItem (hMenu, ID_EDIT_CITY,            MF_BYCOMMAND | (! g_map.Cities.empty ()       ? MF_ENABLED : MF_GRAYED));
				::EnableMenuItem (hMenu, ID_EDIT_STREET,          MF_BYCOMMAND | (cHasAddress != 0              ? MF_ENABLED : MF_GRAYED));
				::EnableMenuItem (hMenu, ID_EDIT_ZIP,             MF_BYCOMMAND | (cHasAddress != 0              ? MF_ENABLED : MF_GRAYED));

				::EnableMenuItem (hMenu, ID_EDIT_JOIN_OBJECTS,    MF_BYCOMMAND | (IsJoinObjectsAppliable     () ? MF_ENABLED : MF_GRAYED));

				::EnableMenuItem (hMenu, ID_EDIT_MERGE_POLYLINES, MF_BYCOMMAND | (IsMergePolylinesAppliable  () ? MF_ENABLED : MF_GRAYED));
				::EnableMenuItem (hMenu, ID_EDIT_CLOSE_POLYLINE,  MF_BYCOMMAND | (IsClosePolylineAppliable   () ? MF_ENABLED : MF_GRAYED));

				if (cPolygons + cPolylines == 0) {
					::DeleteMenu (hMenu, ID_TOOL_EDIT_NODES, MF_BYCOMMAND);
					DeleteDuplicatedMenuSeparators (hMenu);
				}

				::EnableMenuItem (hMenu, ID_SELECT_INSIDE_POLYGON, MF_BYCOMMAND | (g_Selection.size () == 1 ? MF_ENABLED : MF_GRAYED));
				if (! g_Selection [0].iRgn->IsPolygon ()) {
					::DeleteMenu (hMenu, ID_SELECT_INSIDE_POLYGON, MF_BYCOMMAND);
					DeleteDuplicatedMenuSeparators (hMenu);
				}

				::EnableMenuItem (hMenu, ID_EDIT_REVERSE_NODES,   MF_BYCOMMAND | (IsReversePolylineAppliable () ? MF_ENABLED : MF_GRAYED));
				if (cPolylines == 0 && cPolylinesE == 0) {
					::DeleteMenu (hMenu, ID_EDIT_MERGE_POLYLINES, MF_BYCOMMAND);
					::DeleteMenu (hMenu, ID_EDIT_CLOSE_POLYLINE,  MF_BYCOMMAND);
					::DeleteMenu (hMenu, ID_EDIT_REVERSE_NODES,   MF_BYCOMMAND);
					DeleteDuplicatedMenuSeparators (hMenu);
				}

				if (cRoads == 0) {
					if (cPolylines != 0)
						DisableParentMenu (hMenu, ID_EDIT_SPEED_LIMIT);
					else {
						DeleteParentMenu (hMenu, ID_EDIT_SPEED_LIMIT);
						DeleteDuplicatedMenuSeparators (hMenu);
					}
				}

				bool bEnableExtract = false;
				if (cSelItems == 1 && g_Selection [0].pLayer && g_Selection [0].cElementOrOrigin != -1) {
					if (
						g_Selection [0].iRgn->GetElements () != 1 &&
						g_Selection [0].iRgn->GetOrigins  () != 1
					)
						bEnableExtract = true;
				}
				::EnableMenuItem (hMenu, ID_EDIT_EXTRACT, MF_BYCOMMAND | (bEnableExtract ? MF_ENABLED : MF_GRAYED));

				::DeleteMenu (hMenu, ID_EDIT_TRIM_OUTSIDE,       MF_BYCOMMAND); // NOTE: not ready yet
				::DeleteMenu (hMenu, ID_EDIT_TRIM_INSIDE,        MF_BYCOMMAND); // NOTE: not ready yet
				::DeleteMenu (hMenu, ID_EDIT_TRIM_SELECTION,     MF_BYCOMMAND); // NOTE: considered as complex to understand
				::DeleteMenu (hMenu, ID_EDIT_POLYGONS_XOR,       MF_BYCOMMAND); // NOTE: considered as useless
				if (cPolygons > 0) {
					::EnableMenuItem (hMenu, ID_EDIT_POLYGONS_MERGE,     MF_BYCOMMAND | (IsPolygonBooleanApplicable () ? MF_ENABLED : MF_GRAYED));
					::EnableMenuItem (hMenu, ID_EDIT_POLYGONS_INTERSECT, MF_BYCOMMAND | (IsPolygonBooleanApplicable () ? MF_ENABLED : MF_GRAYED));
					::EnableMenuItem (hMenu, ID_EDIT_POLYGONS_SUBTRACT,  MF_BYCOMMAND | (IsPolygonBooleanApplicable () ? MF_ENABLED : MF_GRAYED));
//					::EnableMenuItem (hMenu, ID_EDIT_POLYGONS_XOR,       MF_BYCOMMAND | (IsPolygonBooleanApplicable () ? MF_ENABLED : MF_GRAYED));

//					::EnableMenuItem (hMenu, ID_EDIT_TRIM_SELECTION,     MF_BYCOMMAND | (IsTrimSelectedObjectsApplicable () ? MF_ENABLED : MF_GRAYED));
//					::EnableMenuItem (hMenu, ID_EDIT_TRIM_OUTSIDE,       MF_BYCOMMAND | (IsTrimMapApplicable () ? MF_ENABLED : MF_GRAYED));
//					::EnableMenuItem (hMenu, ID_EDIT_TRIM_INSIDE,        MF_BYCOMMAND | (IsTrimMapApplicable () ? MF_ENABLED : MF_GRAYED));
				} else {
					::DeleteMenu (hMenu, ID_EDIT_POLYGONS_MERGE,     MF_BYCOMMAND);
					::DeleteMenu (hMenu, ID_EDIT_POLYGONS_SUBTRACT,  MF_BYCOMMAND);

//					::DeleteMenu (hMenu, ID_EDIT_TRIM_OUTSIDE,       MF_BYCOMMAND);
//					::DeleteMenu (hMenu, ID_EDIT_TRIM_INSIDE,        MF_BYCOMMAND);

					DeleteDuplicatedMenuSeparators (hMenu);
				}
			}
		}
	}

	if (hMenu == NULL && g_bShowGoogleMaps) {
		hMenu = ::GetSubMenu (hPopupMenu, smpGoogle);
		::EnableMenuItem (hMenu, ID_GM_REFRESH_TILE, (CanRefreshWebMapTile () ? MF_ENABLED : MF_GRAYED) | MF_BYCOMMAND);
	}

	if (hMenu) {
		::EnableMenuItem (hMenu, ID_VIEW_BEST_FIT, MF_BYCOMMAND | (! g_Selection.empty () ? MF_ENABLED : MF_GRAYED));

		PopupMenu (hMenu);
	}
}

bool CSelectTool::OnCommand (WORD _wID) {
	switch (_wID) {
		case ID_EDIT_TYPE:          ShowChangeTypeForSelection (g_hWnd);             return true;
		case ID_EDIT_KIND_POLYLINE: ShowChangeTypeForSelection (g_hWnd, rkPolyline); return true;
		case ID_EDIT_KIND_POLYGON:  ShowChangeTypeForSelection (g_hWnd, rkPolygon);  return true;

		case ID_EDIT_LABEL: {
			if (g_Selection.empty ())
				return false;

			if (g_Selection.size () == 1 && g_Selection [0].pLayer && g_Selection [0].cElementOrOrigin == -1) {
				OnEditLabelStart ();
				return true;
			}

			CCritSec::CLock lock (g_cs);

			// Get initial value for the label.
			string_t strLabel;

			const size_t cSelItems = g_Selection.size ();
			size_t cSelItem;
			for (cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
				const find_info_t & SelItem = g_Selection [cSelItem];
				if (
					SelItem.pLayer && ! SelItem.iRgn->IsDeleted () && ! SelItem.iRgn->IsWTR () &&
					SelItem.cElementOrOrigin == -1
				)
					if (strLabel.empty ())
						strLabel = SelItem.iRgn->strLabel;
					else if (! (strLabel == SelItem.iRgn->strLabel)) {
						strLabel = string_t ();
						break;
					}
			}

			// Edit label.
			if (! EditText (g_hWnd, strLabel, GetStr (IDS_EDIT_LABEL), false))
				return true;

			// Change labels for multiple objects.
			const int iDepth = BeginCombinedAction ();

			for (cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
				const find_info_t & SelItem = g_Selection [cSelItem];
				if (
					SelItem.pLayer && ! SelItem.iRgn->IsDeleted () && ! SelItem.iRgn->IsWTR () &&
					SelItem.cElementOrOrigin == -1
				)
					RenameObject (* SelItem.iRgn, strLabel);
			}

			EndCombinedAction (iDepth, GetStr (IDS_CHANGE_LABEL_MULTIPLE));

			Refresh ();
			
			return true;
		}

		case ID_EDIT_COMMENT: {
			if (g_Selection.empty ())
				return false;

			CCritSec::CLock lock (g_cs);

			// Get initial value for the comment.
			string_t strComment;

			const size_t cSelItems = g_Selection.size ();
			size_t cSelItem;
			for (cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
				const find_info_t & SelItem = g_Selection [cSelItem];
				if (
					SelItem.pLayer && ! SelItem.iRgn->IsDeleted () && ! SelItem.iRgn->IsWTR () &&
					SelItem.cElementOrOrigin == -1
				)
					if (strComment.empty ())
						strComment = SelItem.iRgn->strComment;
					else if (! (strComment == SelItem.iRgn->strComment)) {
						strComment = string_t ();
						break;
					}
			}

			// Edit comment.
			if (! EditText (g_hWnd, strComment, GetStr (IDS_EDIT_COMMENT), true))
				return true;

			// Change comments for multiple objects.
			const int iDepth = BeginCombinedAction ();

			for (cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
				const find_info_t & SelItem = g_Selection [cSelItem];
				if (
					SelItem.pLayer && ! SelItem.iRgn->IsDeleted () && ! SelItem.iRgn->IsWTR () &&
					SelItem.cElementOrOrigin == -1
				)
					SetComment (* SelItem.iRgn, strComment);
			}

			EndCombinedAction (iDepth, GetStr (IDS_CHANGE_COMMENT_MULTIPLE));

			Refresh ();
			
			return true;
		}

		case ID_EDIT_END_LEVEL: {
			if (g_Selection.empty ())
				return false;

			CCritSec::CLock lock (g_cs);

			// Get initial value for levels.
			size_t cEndLevel = 0;

			const size_t cSelItems = g_Selection.size ();
			size_t cSelItem;
			for (cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
				const find_info_t & SelItem = g_Selection [cSelItem];
				if (
					SelItem.pLayer && ! SelItem.iRgn->IsDeleted () && ! SelItem.iRgn->IsWTR () &&
					SelItem.cElementOrOrigin == -1
				)
					if (cEndLevel < SelItem.iRgn->uchEndLevel)
						cEndLevel = SelItem.iRgn->uchEndLevel;
			}

			// Edit levels value.
			if (! EditNumber (g_hWnd, cEndLevel, GetStr (IDS_SET_LEVELS_REQ)))
				return true;

			// Change levels for multiple objects.
			const int iDepth = BeginCombinedAction ();

			for (cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
				const find_info_t & SelItem = g_Selection [cSelItem];
				if (
					SelItem.pLayer && ! SelItem.iRgn->IsDeleted () && ! SelItem.iRgn->IsWTR () &&
					SelItem.cElementOrOrigin == -1
				)
					SetObjectEndLevel (* SelItem.iRgn, cEndLevel);
			}

			EndCombinedAction (iDepth, GetStr (IDS_SET_LEVELS_MULTIPLE));

			Refresh ();
			
			return true;
		}

		case ID_EDIT_CITY: {
			if (g_Selection.empty ())
				return false;

			CCritSec::CLock lock (g_cs);

			// Get initial value for cities.
			city_t * pCitySel = NULL;
			bool bIniSel = true;
			bool bFirst = true;

			const size_t cSelItems = g_Selection.size ();
			size_t cSelItem;
			for (cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
				const find_info_t & SelItem = g_Selection [cSelItem];
				if (
					SelItem.pLayer && ! SelItem.iRgn->IsDeleted () && ! SelItem.iRgn->IsWTR () &&
					SelItem.cElementOrOrigin == -1
				) {
					city_t * pCity = SelItem.iRgn->pExtra ? SelItem.iRgn->pExtra->pCity : NULL;
					if (pCity && pCity->IsDeleted ())
						pCity = NULL;

					if (bFirst) {
						pCitySel = pCity;
						bFirst = false;
					} else {
						if (pCitySel != pCity) {
							pCitySel = NULL;
							bIniSel = false;
							break;
						}
					}
				}
			}

			// Edit city value.
			if (! SelectCity (g_hWnd, bIniSel, pCitySel))
				return true;

			// Change cities for multiple objects.
			const int iDepth = BeginCombinedAction ();

			for (cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
				const find_info_t & SelItem = g_Selection [cSelItem];
				if (
					SelItem.pLayer && ! SelItem.iRgn->IsDeleted () && ! SelItem.iRgn->IsWTR () &&
					SelItem.cElementOrOrigin == -1
				)
					ChangeCity (* SelItem.iRgn, pCitySel);
			}

			EndCombinedAction (iDepth, GetStr (IDS_CHANGE_CITY_MULTIPLE));

			Refresh ();
			
			return true;
		}

		case ID_EDIT_STREET: {
			if (g_Selection.empty ())
				return false;

			CCritSec::CLock lock (g_cs);

			// Get initial value for street.
			string_t strStreet;

			const size_t cSelItems = g_Selection.size ();
			size_t cSelItem;
			for (cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
				const find_info_t & SelItem = g_Selection [cSelItem];
				if (
					SelItem.pLayer && ! SelItem.iRgn->IsDeleted () && ! SelItem.iRgn->IsWTR () &&
					SelItem.cElementOrOrigin == -1
				)
					if (
						g_map.pTypeSet->pfHasAddress (* SelItem.iRgn) &&
						SelItem.iRgn->pExtra && ! SelItem.iRgn->pExtra->strStreetDesc.empty ()
					) {
						if (strStreet.empty ())
							strStreet = SelItem.iRgn->pExtra->strStreetDesc;
						else if (! (strStreet == SelItem.iRgn->pExtra->strStreetDesc)) {
							strStreet = string_t ();
							break;
						}
					}
			}

			// Edit street.
			if (! EditText (g_hWnd, strStreet, GetStr (IDS_EDIT_STREET), false))
				return true;

			// Change street for multiple objects.
			const int iDepth = BeginCombinedAction ();

			for (cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
				const find_info_t & SelItem = g_Selection [cSelItem];
				if (
					SelItem.pLayer && ! SelItem.iRgn->IsDeleted () && ! SelItem.iRgn->IsWTR () &&
					SelItem.cElementOrOrigin == -1
				)
					if (g_map.pTypeSet->pfHasAddress (* SelItem.iRgn))
						SetObjectPAStreet (* SelItem.iRgn, strStreet);
			}

			EndCombinedAction (iDepth, GetStr (IDS_CHANGE_STREET_MULTIPLE));

			Refresh ();
			
			return true;
		}

		case ID_EDIT_ZIP: {
			if (g_Selection.empty ())
				return false;

			CCritSec::CLock lock (g_cs);

			// Get initial value for zip codes.
			zip_t * pZip = NULL;

			const size_t cSelItems = g_Selection.size ();
			size_t cSelItem;
			for (cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
				const find_info_t & SelItem = g_Selection [cSelItem];
				if (
					SelItem.pLayer && ! SelItem.iRgn->IsDeleted () && ! SelItem.iRgn->IsWTR () &&
					SelItem.cElementOrOrigin == -1
				)
					if (SelItem.iRgn->pExtra && SelItem.iRgn->pExtra->pZip) {
						if (! pZip)
							pZip = SelItem.iRgn->pExtra->pZip;
						else if (pZip != SelItem.iRgn->pExtra->pZip) {
							pZip = NULL;
							break;
						}
					}
			}

			// Edit zip code value.
			if (! ShowChangeZip (g_hWnd, pZip))
				return true;

			// Change zip code for multiple objects.
			const int iDepth = BeginCombinedAction ();

			for (cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
				const find_info_t & SelItem = g_Selection [cSelItem];
				if (
					SelItem.pLayer && ! SelItem.iRgn->IsDeleted () && ! SelItem.iRgn->IsWTR () &&
					SelItem.cElementOrOrigin == -1
				)
					ChangeZip (* SelItem.iRgn, pZip);
			}

			EndCombinedAction (iDepth, GetStr (IDS_CHANGE_ZIP_MULTIPLE));

			Refresh ();
			
			return true;
		}

		case ID_EDIT_ROUTE_CLASS: {
			if (g_Selection.empty ())
				return false;

			CCritSec::CLock lock (g_cs);

			// Get initial value for route class.
			int nRouteClassSel = -1;
			bool bFirst = true;

			const size_t cSelItems = g_Selection.size ();
			size_t cSelItem;
			for (cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
				const find_info_t & SelItem = g_Selection [cSelItem];
				if (
					SelItem.pLayer && ! SelItem.iRgn->IsDeleted () && SelItem.iRgn->IsPolyline () &&
					SelItem.cElementOrOrigin == -1
				) {
					if (! g_map.pTypeSet->pfIsRoad (* SelItem.iRgn))
						continue;
					const int nRouteClass = SelItem.iRgn->pExtra ? SelItem.iRgn->pExtra->fRouteClass : -1;

					if (bFirst) {
						nRouteClassSel = nRouteClass;
						bFirst = false;
					} else {
						if (nRouteClassSel != nRouteClass) {
							nRouteClassSel = -1;
							break;
						}
					}
				}
			}

			// Select different route class.
			static const CListBoxItem s_lbiRouteClasses [] = {
				{IDS_ROUTE_CLASS_0, 0},
				{IDS_ROUTE_CLASS_1, 1},
				{IDS_ROUTE_CLASS_2, 2},
				{IDS_ROUTE_CLASS_3, 3},
				{IDS_ROUTE_CLASS_4, 4},
			};
			if (! SelectFromList (g_hWnd, IDS_ROUTE_CLASSES_TITLE, s_lbiRouteClasses, sizeof (s_lbiRouteClasses)/sizeof (s_lbiRouteClasses [0]), nRouteClassSel))
				return true;

			// Change route class for multiple objects.
			const int iDepth = BeginCombinedAction ();

			for (cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
				const find_info_t & SelItem = g_Selection [cSelItem];
				if (
					SelItem.pLayer && ! SelItem.iRgn->IsDeleted () && SelItem.iRgn->IsPolyline () &&
					SelItem.cElementOrOrigin == -1
				) {
					if (! g_map.pTypeSet->pfIsRoad (* SelItem.iRgn))
						continue;

					SetObjectRouteClass (* SelItem.iRgn, nRouteClassSel);
				}
			}

			EndCombinedAction (iDepth, GetStr (IDS_CHANGE_ROUTE_CLASS_MULTIPLE));

			Refresh ();

			break;
		}

		case ID_EDIT_SPEED_LIMIT: {
			if (g_Selection.empty ())
				return false;

			CCritSec::CLock lock (g_cs);

			// Get initial value for speed limit.
			int nSpeedLimitSel = -1;
			bool bFirst = true;

			const size_t cSelItems = g_Selection.size ();
			size_t cSelItem;
			for (cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
				const find_info_t & SelItem = g_Selection [cSelItem];
				if (
					SelItem.pLayer && ! SelItem.iRgn->IsDeleted () && SelItem.iRgn->IsPolyline () &&
					SelItem.cElementOrOrigin == -1
				) {
					if (! g_map.pTypeSet->pfIsRoad (* SelItem.iRgn))
						continue;
					const int nSpeedLimit = SelItem.iRgn->pExtra ? SelItem.iRgn->pExtra->fSpeedLimit : -1;

					if (bFirst) {
						nSpeedLimitSel = nSpeedLimit;
						bFirst = false;
					} else {
						if (nSpeedLimitSel != nSpeedLimit) {
							nSpeedLimitSel = -1;
							break;
						}
					}
				}
			}

			// Select different speed limit.
			static const CListBoxItem s_lbiSpeedLimits [] = {
				{IDS_SPEED_LIMIT_0, 0},
				{IDS_SPEED_LIMIT_1, 1},
				{IDS_SPEED_LIMIT_2, 2},
				{IDS_SPEED_LIMIT_3, 3},
				{IDS_SPEED_LIMIT_4, 4},
				{IDS_SPEED_LIMIT_5, 5},
				{IDS_SPEED_LIMIT_6, 6},
				{IDS_SPEED_LIMIT_7, 7},
			};
			if (! SelectFromList (g_hWnd, IDS_SPEED_LIMITS_TITLE, s_lbiSpeedLimits, sizeof (s_lbiSpeedLimits)/sizeof (s_lbiSpeedLimits [0]), nSpeedLimitSel))
				return true;

			// Change speed limit for multiple objects.
			const int iDepth = BeginCombinedAction ();

			for (cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
				const find_info_t & SelItem = g_Selection [cSelItem];
				if (
					SelItem.pLayer && ! SelItem.iRgn->IsDeleted () && SelItem.iRgn->IsPolyline () &&
					SelItem.cElementOrOrigin == -1
				) {
					if (! g_map.pTypeSet->pfIsRoad (* SelItem.iRgn))
						continue;

					SetObjectSpeedLimit (* SelItem.iRgn, nSpeedLimitSel);
				}
			}

			EndCombinedAction (iDepth, GetStr (IDS_CHANGE_SPEED_LIMIT_MULTIPLE));

			Refresh ();

			break;
		}
/*
		case ID_EDIT_GENERALIZE:
			GeneralizeSelection ();
			Refresh ();
			return true;
*/
		case ID_EDIT_JOIN_OBJECTS:
			JoinSelectedObjects ();
			Refresh ();
			return true;

		case ID_EDIT_EXTRACT:
			if (g_Selection.size () == 1 && g_Selection [0].pLayer && g_Selection [0].cElementOrOrigin != -1) {
				ExtractElement (* g_Selection [0].iRgn, g_Selection [0].cElementOrOrigin);
				Refresh ();
			}
			return true;

		case ID_EDIT_MERGE_POLYLINES:    MergeSelectedPolylines ();    Refresh (); return true;
		case ID_EDIT_CLOSE_POLYLINE:     CloseSelectedPolylines ();    Refresh (); return true;
		case ID_EDIT_REVERSE_NODES:      ReverseSelectedPolylines ();  Refresh (); return true;

		case ID_EDIT_POLYGONS_MERGE:     MergeSelectedPolygons ();     Refresh (); return true;
		case ID_EDIT_POLYGONS_INTERSECT: IntersectSelectedPolygons (); Refresh (); return true;
		case ID_EDIT_POLYGONS_SUBTRACT:  SubtractSelectedPolygons ();  Refresh (); return true;
		case ID_EDIT_POLYGONS_XOR:       XorSelectedPolygons ();       Refresh (); return true;

		case ID_EDIT_TRIM_SELECTION:     TrimSelectedObjects ();       Refresh (); return true;
		case ID_EDIT_TRIM_OUTSIDE:       TrimMap (taKeepInside);       Refresh (); return true;
		case ID_EDIT_TRIM_INSIDE:        TrimMap (taKeepOutside);      Refresh (); return true;

		case ID_SELECT_INSIDE_POLYGON: {
			if (g_Selection.size () != 1 || ! g_Selection [0].pLayer)
				return false;
			rgn_t & rgn = * g_Selection [0].iRgn;
			SelectObjectsInsidePolygon (rgn, true);
			Redraw ();
			return true;
		}

		case ID_EDIT_VECTORIZE:
			if (g_Selection.size () == 1 && g_Selection [0].pRenderer && g_Selection [0].pRenderer->IsImage ())
				Vectorize (g_lRClickX, g_lRClickY, static_cast<const image_t *> (g_Selection [0].pRenderer));
			return true;

		case ID_EDIT_REVERT_CORRECTION:
			if (g_Selection.size () == 1 && g_Selection [0].pRenderer) {
				MoveImage (
					g_Selection [0].pRenderer,
					point_t (
						- g_Selection [0].pRenderer->ptCorrectionShift.x,
						- g_Selection [0].pRenderer->ptCorrectionShift.y
					)
				);

				Refresh ();
			}
			return true;

		case ID_EDIT_CONVERT_TO_POI:
		case ID_EDIT_CONVERT_TO_POLYLINE:
		case ID_EDIT_CONVERT_TO_POLYGON: {
			if (g_Selection.empty ())
				return false;

			CCritSec::CLock lock (g_cs);

			// Prepare levels to import the track.
			levels_t Levels;
			bool bGeneralize = _wID != ID_EDIT_CONVERT_TO_POI;
			if (g_map.Levels.empty ())
				Levels.insert (0);
			else {
				// Ask user to select levels to import track (the current level is default).
				Levels.insert (GetZoomLevel (g_fScale));
				if (! SelectLevels (g_hWnd, _wID != ID_EDIT_CONVERT_TO_POI, Levels, bGeneralize))
					break;
				if (Levels.empty ())
					break;
			}

			// Prepare kind.
			RGN_KIND Kind;
			switch (_wID) {
				case ID_EDIT_CONVERT_TO_POI:      Kind = rkPoint;    break;
				case ID_EDIT_CONVERT_TO_POLYLINE: Kind = rkPolyline; break;
				case ID_EDIT_CONVERT_TO_POLYGON:  Kind = rkPolygon;  break;
				default: assert (false);
			}

			// Convert multiple objects.
			const int iDepth = BeginCombinedAction ();

			const size_t cSelItems = g_Selection.size ();
			for (size_t cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
				const find_info_t & SelItem = g_Selection [cSelItem];
				if (SelItem.pLayer && ! SelItem.iRgn->IsDeleted () && SelItem.iRgn->IsWTR ())
					ConvertWaypointOrTrack (Levels, & * SelItem.iRgn, Kind, bGeneralize);
			}

			EndCombinedAction (iDepth, GetStr (IDS_CONVERT_MULTIPLE));

			Refresh ();

			// Change type.
			ShowChangeTypeForSelection (g_hWnd, Kind);

			return true;
		}

		case ID_VIEW_SHOW_DIRECTION: {
			if (g_Selection.size () != 1)
				return false;

			rgn_t & rgn = * g_Selection [0].iRgn;
			if (rgn.IsWTR () && rgn.pFileRef) {
				rgn.pFileRef->bShowDirection = ! rgn.pFileRef->bShowDirection;
				Refresh ();
			}

			return true;
		}

		case ID_VIEW_HIDE: {
			if (g_Selection.empty ())
				return false;

			const size_t cSelItems = g_Selection.size ();
			for (size_t cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
				const find_info_t & SelItem = g_Selection [cSelItem];
				if (SelItem.pLayer && ! SelItem.iRgn->IsDeleted () && SelItem.iRgn->IsWTR () && SelItem.iRgn->pFileRef && SelItem.iRgn->pFileRef->bVisible) {
					ShowAttachment (SelItem.iRgn->pFileRef, false, false);
					Refresh ();
					return true;
				} else {
					if (! SelItem.pRenderer->bDeleted && SelItem.pRenderer->bVisible) {
						ShowAttachment (SelItem.pRenderer, false, true);
						Refresh ();
						return true;
					}
				}
			}

			return true;
		}

		case ID_VIEW_BEST_FIT: {
			if (g_Selection.empty ())
				return false;

			rect_t rect;
			const size_t cSelItems = g_Selection.size ();
			for (size_t cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
				const find_info_t & SelItem = g_Selection [cSelItem];
				if (SelItem.pLayer && ! SelItem.iRgn->IsDeleted ()) {
					if (SelItem.cElementOrOrigin != -1)
						rect.Extend (SelItem.iRgn->elements [SelItem.cElementOrOrigin].rectBound);
					else
						rect.Extend (SelItem.iRgn->rectBound);
				} else {
					if (! SelItem.pRenderer->bDeleted)
						rect.Extend (SelItem.pRenderer->rect);
				}
			}
			if (rect.Invalid ())
				return true;

			SetScale (GetRectScale (rect));
			SetToCenter (point_t (rect.MeanX (), rect.MeanY ()));
			Refresh ();

			return true;
		}

		case ID_GM_REFRESH_TILE: RefreshWebMapTile (g_lRClickX, g_lRClickY); break;
		case ID_GM_SHIFT:        ShiftWebMap (g_hWnd); break;
		case ID_GM_RESET_SHIFT:  ResetWebMapShift (); break;
	}
	return false;
}

void CSelectTool::OnLDblClk (int _x, int _y, DWORD _dwKeys) {
	SelectObjectAt (_x, _y, _IsSelMultipleOn (_dwKeys), _IsSelElementOn (_dwKeys));
	if (g_Selection.size () == 1) {
		if (g_Selection [0].pLayer)
			ShowRgnProperties (g_hWnd, g_Selection [0]);
		else
			ShowRasterProperties (g_hWnd, g_Selection [0]);
	}
}

void CSelectTool::OnPaint (HDC _hDC, const RECT & _rect) {
    if (! CTool::s_bDND)
		return;

    if (g_bFrameSelectionMode) {
	    ::SelectObject (_hDC, g_hpenGroupSelection);
		::SetROP2 (_hDC, R2_COPYPEN);

        POINT ptSelRect [5] = {
            {CTool::s_lX_DND_Start, CTool::s_lY_DND_Start},
            {CTool::s_lX_DND_Start, CTool::s_lY_DND},
            {CTool::s_lX_DND,       CTool::s_lY_DND},
            {CTool::s_lX_DND,       CTool::s_lY_DND_Start},
            {CTool::s_lX_DND_Start, CTool::s_lY_DND_Start}
        };
        ::Polyline (_hDC, ptSelRect, 5);
    }
}
